package com.omni.monitor.plugin.trace_monitor;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.omni.monitor.model.trace_monitor.TraceNode;
import com.omni.monitor.plugin.utils.MonitorConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJAroundAdvice;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.SingletonAspectInstanceFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

@Aspect
@Component
@Slf4j
public class ServiceTraceAspect {
    private final TraceContext traceContext;
    private final MonitorConfig monitorConfig;

    public ServiceTraceAspect(TraceContext traceContext, MonitorConfig monitorConfig) {
        this.traceContext = traceContext;
        this.monitorConfig = monitorConfig;

    }

    // 动态创建切入点和Advisor
    @Bean
    // 仅当配置项 monitor.enabled 的值为 "on" 时，才创建该bean
    @ConditionalOnProperty(name = "trace-monitor.enabled",  // 配置项名称
            havingValue = "on",        // 期望的值
            matchIfMissing = false     // 若配置项不存在，默认不创建
    )
    public Advisor dynamicPointcutAdvisor() throws NoSuchMethodException {
        // 1. 获取配置的切入点表达式
        String expression = monitorConfig.pointcutExpression();
        if (expression == null || expression.trim().isEmpty()) {
            throw new IllegalArgumentException("切入点表达式不能为空，请检查配置");
        }
        // 1. 获取配置的切入点表达式
        String optimizedExpression = expression + " && !within($Proxy*) && !within(org.springframework..*)";
        // 2. 创建切入点并设置表达式
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(optimizedExpression); // 从配置读取表达式

        // 3. 获取环绕通知方法（traceServiceCall）
        Method adviceMethod = ServiceTraceAspect.class.getMethod("traceServiceCall", ProceedingJoinPoint.class);

        // 4. 关键修复：用SingletonAspectInstanceFactory包装切面实例
        // 解决 "ServiceTraceAspect cannot be cast to AspectInstanceFactory" 错误
        SingletonAspectInstanceFactory aspectInstanceFactory = new SingletonAspectInstanceFactory(this);

        // 5. 创建环绕通知（使用正确的工厂类）
        AspectJAroundAdvice aroundAdvice = new AspectJAroundAdvice(adviceMethod, pointcut, aspectInstanceFactory // 传入工厂类而非直接传入this
        );

        // 6. 创建Advisor并关联切入点和通知
        return new DefaultPointcutAdvisor(pointcut, aroundAdvice);
    }


    /**
     * 精准切点：拦截所有@Service注解类的公共方法（排除私有/静态方法）
     *
     * @within(org.springframework.stereotype.Service) && execution(public * *(..))
     */
    // @Around("methodPointcut()")
    public Object traceServiceCall(ProceedingJoinPoint joinPoint) throws Throwable {
        // 1. 获取真实目标类（排除CGLIB/JDK代理）
        Class<?> targetClass = ClassUtils.getUserClass(joinPoint.getTarget());
        String serviceName = targetClass.getSimpleName();

        // 2. 获取方法名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String methodName = signature.getName();
        TraceNode currentNode = enterService(joinPoint, serviceName, targetClass, methodName, signature);
        Object result = null;
        boolean success = true;
        try {
            // 5. 执行原方法
            result = joinPoint.proceed();
            return result;
        } catch (Throwable e) {
            success = false;
            // 6. 捕获异常：记录错误日志（含调用链三要素）
            log.error("Service调用失败：{}.{}，错误信息：{}", serviceName, methodName, e.getMessage(), e); // 打印堆栈，方便排查
            throw e;
        } finally {

            exitService(signature, result, currentNode, success);
        }
    }


    private TraceNode enterService(ProceedingJoinPoint joinPoint, String serviceName, Class<?> targetClass, String methodName, MethodSignature signature) {
        TraceNode currentNode = null;
        // 判断是否开启监控
        if (!monitorConfig.getEnabled().equals("on")) {
            return null;
        }
        try {
            if (serviceName.startsWith("$Proxy")) {
                // 获取代理类实现的所有接口
                Class<?>[] interfaces = targetClass.getInterfaces();
                for (Class<?> iface : interfaces) {
                    // 排除父接口（如 BaseMapper），找到我们自定义的业务接口
                    // 可根据实际包名或接口特征调整筛选条件
                    if (iface.getName().endsWith("Mapper") && !iface.getName().equals(BaseMapper.class.getName())) {
                        serviceName = iface.getSimpleName();
                        break;
                    }
                }
            }
            if (monitor(serviceName, methodName)) {
                // 3. 获取方法入参
                //入参值
                Object[] args = joinPoint.getArgs();
                // 入参类型
                Class<?>[] parameterTypes = signature.getParameterTypes();
                String argsStr = args != null ? Arrays.toString(args) : "无参数";
                // 4. 进入Service：压栈并生成spanId
                Map<String, Object> params = null;
                if (monitorConfig.getParameterSwitch().equals("on")) {
                    params = new HashMap<>();
                    for (int i = 0; i < parameterTypes.length; i++) {

                        if (parameterTypes[i] == null) {
                            continue;
                        }
                        if (args != null) {
                            params.put(parameterTypes[i].getSimpleName(), args[i]);
                        } else {
                            params.put(parameterTypes[i].getSimpleName(), "null");
                        }

                    }
                }

                currentNode = traceContext.enterService(serviceName, methodName, params);
            }
        } catch (Exception e) {
            // 6. 捕获异常：记录错误日志（含调用链三要素）
            log.error("【enterService】方法调用失败：{}.{}，错误信息：{}", serviceName, methodName, e.getMessage(), e); // 打印堆栈，方便排查
            throw e;
        }


        return currentNode;
    }

    private void exitService(MethodSignature signature, Object result, TraceNode currentNode, boolean success) {
        // 判断是否开启监控
        if (!monitorConfig.getEnabled().equals("on")) {
            return;
        }
        try {
            if (currentNode == null) {
                return;
            }
            Map<String, Object> resultMap = null;
            Class<?> returnType = signature.getReturnType(); // 返回值类型
            if (monitorConfig.getParameterSwitch().equals("on")) {
                resultMap = new HashMap<>();
                resultMap.put(returnType.getSimpleName(), result);
            }
            currentNode.setSuccess(success);
            // 7. 退出Service：弹栈并恢复父节点MDC
            traceContext.exitService(currentNode, resultMap);
        } catch (Exception e) {
            // 6. 捕获异常：记录错误日志（含调用链三要素）
            log.error("【exitService】方法调用失败：{}.{}，错误信息：{}", currentNode.getServiceName(), currentNode.getMethodName(), e.getMessage(), e); // 打印堆栈，方便排查
            throw e;
        }

    }

    private boolean monitor(String serviceName, String methodName) {
        boolean flag = true;
        if (StringUtils.isNotBlank(monitorConfig.getExclude_service_method())
                && (monitorConfig.getExclude_service_method().contains(serviceName + "." + methodName)
                    || monitorConfig.getExclude_service_method().contains(serviceName + ".*"))) {
            flag = false;
        }
        return flag;
    }
}

